import json

from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponse
import re

from django.http import JsonResponse
from django.shortcuts import render, redirect
from django.urls import reverse
from django.views import View
from django import http
from django_redis import get_redis_connection
from rest_framework.generics import ListCreateAPIView

from apps.areas.models import Area
from apps.goods.models import SKU
from apps.statistical.serializers import UserSerializer
from apps.users.models import User, Address
from apps.users.utils import generate_verify_email_url
from apps.verifications import constants
from meiduo_mall.settings.dev import logger
from utils.response_code import RETCODE
from utils.secret import SecretOauth

# 用户注册

class RegisterView(View):
    """用户注册"""
    def get(self,request):
        return render(request, 'register.html')
    def post(self,request):
        # 接受参数
        username = request.POST.get('username')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        mobile = request.POST.get('mobile')
        # 是否勾选用户协议
        allow = request.POST.get('allow')
        # 短信验证码
        sms_code = request.GET.get('msg_code')
        # 判断参数是否齐全
        if not all([username, password, password2, mobile,]):
            return http.HttpResponseForbidden('缺少必传参数')
        # (正则校验)判断用户名是否是5-20个字符
        # re.match()判断字符串是否符合正则 返回Tuer或False
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden("请输入8-20位的密码")
        # (正则校验)判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseForbidden('请输入8-20位的密码')
        # 判断两次密码是否一致
        if password != password2:
            return http.HttpResponseForbidden('两次输入的密码不一致')
        # (正则校验)判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('请输入正确的手机号码')
        # 判断是否勾选用户协议
        if allow != 'on':
            return http.HttpResponseForbidden('请勾选用户协议')
        # 与数据库建立链接
        redis_code_client = get_redis_connection('sms_code')
        # 取数据
        # redis_code = redis_code_client.get("sms_%s" % mobile)
        # 验证短信验证码是否正确;
        # # 对比redis的值和前端的值
        # if redis_code is None:
        #     return render(request, 'register.html', {'sms_code_errmsg': '无效的短信验证码'})
        # #
        # if sms_code != redis_code.decode():
        #     return render(request, 'register.html', {'sms_code_errmsg': '输入短信验证码有误'})
        # 保存注册数据
        user = User.objects.create_user(username=username, password=password, mobile=mobile)
        # 保持登录
        login(request, user)
        # 重定向到主界面
        return redirect(reverse('contents:index'))

# 判断用户名是否重复注册

class UsernameCountView(View):

    def get(self, request, username):
        """
        :param request: 请求对象
        :param username: 用户名
        :return: JSON
        """
        # 从数据库中查找同名用户个数
        count = User.objects.filter(username=username).count()
        # 返回JOSN
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})

# 判断手机号是否重复注册

class MobileCountView(View):

    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()
        return http.JsonResponse({'code': 'RETCODE.OK', 'errmsg': 'OK', 'count': count})

# 用户登录

class LoginView(View):
    def get(self,request):
        return render(request, 'login.html')
    def post(self,request):
        # 1 后台接受3个数据
        # 2 from表单  非表单 表头(都可以传参)
        # 5 登录跳转主页面
        username = request.POST.get('username')
        password = request.POST.get('password')
        remembered = request.POST.get('remembered')
        # 3 校验 判断正则 没有写
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden("请输入8-20位的密码")
            # (正则校验)判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return http.HttpResponseForbidden('请输入8-20位的密码')

        from django.contrib.auth import authenticate, login
        # 4 判断用户名,密码是否正确
        user = authenticate(request,username=username, password=password)
        if user is None:
            return render(request,'login.html',{'account_errmsg': '用户名或密码错误'})
        # 保持登录
        login(request,user)
        # 如果不记住登录
        if remembered != "on":
            # session的保存时间为0s
            request.session.set_expiry(0)
        else:
            # 记住用户名, 浏览器会话保持两周  默认(None)两周
            request.session.set_expiry(None)
        # ?next=/info/作用:从哪里点击登录登录完成后跳转到哪里
        # 操作 next
        next = request.GET.get('next')
        if next:
            # 重定向会next
            response = redirect(next)
        else:
            # 重定向到首页
            response = redirect(reverse('contents:index'))
        # 存用户名到 cookie 里面去 保证保持登录
        response.set_cookie('username', user.username, max_age=2 * 14 * 24 * 3600)
        # 登录成功后合并购物车
        from apps.carts.utils import merge_cart_cookie_to_redis
        merge_cart_cookie_to_redis(request,response)
        # 5.跳转到首页
        return response

# 退出登录

class LogoutView(View):
    def get(self,request):
        # 清除登录状态
        logout(request)
        # 重定向到首页
        response = redirect(reverse("contents:index"))
        # 清除cookie  删除主页用户名
        response.delete_cookie('username')
        return response

# 用户中心的展示

class UserInfoView(LoginRequiredMixin, View):
    """用户中心"""
    # 提供个人信息界面
    # render返回数据应该用jinja2渲染
    # jsonresponse返回数据应该用vue渲染
    # render转换成vue渲染过程:render--jinja2--let--vue
    def get(self, request):
# =====当前用户对象:request.user=========
        context = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_active': request.user.email_active
        }
        print(context)
        return render(request, 'user_center_info.html', context=context)
    # def get(self,request):
    #     context = {
    #         'username':request.user.username
    #     }
    #     return render(request,'文件名',context=context)

# 保存邮箱

class EmailView(LoginRequiredMixin, View):
    """添加邮箱"""
    # json为自己的包
    # 用户访问时理应用put修改,但用户点击后直接生成get请求
    # 函数名:由put更改成get
    def put(self, request):
        """实现添加邮箱逻辑"""
        # 接收参数
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        email = json_dict.get('email')
        # 校验参数
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.HttpResponseForbidden('参数email有误')
        # 赋值email字段
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '添加邮箱失败'})
        # urlencode的使用??
        # 异步发邮件
        # 拼接链接(加密)
        verify_url = generate_verify_email_url(request.user)
        # 开启异步任务
        from celery_tasks.email.tasks import send_verify_email
        send_verify_email.delay(email, verify_url)
        # 响应添加邮箱结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '添加邮箱成功'})

# 激活邮箱(修改邮箱状态)

class EmailVerifyView(LoginRequiredMixin, View):
    def get(self, request):
        # 1.接收参数  request.GET
        token = request.GET.get('token')
        data_dict = SecretOauth().loads(token)
        user_id = data_dict.get('user_id')
        email = data_dict.get('email')
        try:
            user = User.objects.get(id=user_id,email=email)
        except Exception as e:
            print('激活邮箱', e)
            return http.HttpResponseForbidden("token无效的!")
        user.email_active = True
        user.save()
        print("哈哈哈哈")
        return redirect(reverse("users:info"))

# 新增收获地址

class CreateAddressView(LoginRequiredMixin, View):
    """新增地址"""

    def post(self, request):
        """实现新增地址逻辑"""
        # 判断是否超过地址上限：最多20个
        # Address.objects.filter(user=request.user).count()
        count = request.user.addresses.count()
        if count >= constants.USER_ADDRESS_COUNTS_LIMIT:
            return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '超过地址数量上限'})

        # 接收参数
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')

        # 校验参数
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.HttpResponseForbidden('参数email有误')

        # 保存地址信息
        try:
            address = Address.objects.create(
                user=request.user,
                title = receiver,
                receiver = receiver,
                province_id = province_id,
                city_id = city_id,
                district_id = district_id,
                place = place,
                mobile = mobile,
                tel = tel,
                email = email
            )

            # 设置默认地址
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '新增地址失败'})

        # 新增地址成功，将新增的地址响应给前端实现局部刷新
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }

        # 响应保存结果
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '新增地址成功', 'address':address_dict})

# 展示收获地址(用户刷新收仍能显示收获地址)

class AddressView(LoginRequiredMixin, View):
    """用户收货地址"""

    def get(self, request):
        """提供收货地址界面"""
        # 获取用户地址列表
        login_user = request.user
        addresses = Address.objects.filter(user=login_user, is_deleted=False)

        address_dict_list = []
        for address in addresses:
            address_dict = {
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            }
            address_dict_list.append(address_dict)
        context = {
            'default_address_id': login_user.default_address_id,
            'addresses': address_dict_list,
        }

        return render(request, 'user_center_site.html', context)

# 修改密码

class ChangePwdView(LoginRequiredMixin,View):
    def get(self,request):
        return render(request,'user_center_pass.html')
    def post(self,request):
        # 接搜参数
        old_password = request.POST.get('old_pwd')
        new_password = request.POST.get('new_pwd')
        new_password2 = request.POST.get('new_cpwd')
        # 校验判空判正则
        if new_password != new_password2:
            return http.HttpResponseForbidden('两次密码输入不一致')
        # 重设密码
        user = request.user
        if not user.check_password(old_password):
           return render(request,'user_center_pass.html',{'origin_pwd_errmsg': '原始密码错误'})
        user.set_password(new_password)
        user.save()
        # 重定向到首页
        response = redirect(reverse('users:login'))
        # 退出登录
        logout(request)
        response.delete_cookie('username')
        return response

# 修改地址

class UpAddressView(LoginRequiredMixin, View):
    def get(self,request):
        return render(request,'user_center_site.html')
    # 修改地址
    def put(self,request,address_id):
        body = json.loads(request.body.decode())
        print(body)
        receiver = body.get('receiver') # 收件人
        place = body.get('place')   #收获地址
        mobile = body.get('mobile')
        tel = body.get('tel')
        email = body.get('email')
        province_id = body.get('province_id')
        city_id = body.get('city_id')
        district_id = body.get('district_id')
        # 省市县
        district = body.get('district')
        province = body.get('province')
        city = body.get('city')
        id = body.get('id')
        # if not re.match(r'^1[3-9]\d{9}$', check_mobile):
        #     return http.HttpResponseForbidden('参数mobile有误')
        # if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', check_tel):
        #     return http.HttpResponseForbidden('参数tel有误')
        # if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', check_email):
        #     return http.HttpResponseForbidden('参数email有误')
        # 收货人
        # if not check_receiver:
        #     check_receiver= Address.objects.filter(user=requset).get('receiver')
        # # 地址
        # if not check_place:
        #     check_place = Address.objects.filter(user=requset).get('place')
        # # 手机号
        # if not check_mobile:
        #     check_mobile = Address.objects.filter(user=requset).get('mobile')
        # # 固定电话
        # if not check_tel:
        #     check_tel = Address.objects.filter(user=requset).get('tel')
        # if not check_email:
        #     check_email = Address.objects.filter(user=requset).get('email')
        # if not check_province_id:
        #     check_province_id = Address.objects.filter(user=requset).get('province_id')
        # if not check_city_id:
        #     check_city_id = Address.objects.filter(user=requset).get('city_id')
        # if not check_district_id:
        #     check_district_id = Address.objects.filter(user=requset).get('district_id')
        address = Address.objects.get(id=address_id)
        address.receiver=receiver
        address.place=place
        address.mobile=mobile
        address.tel=tel
        address.email=email
        address.province_id=province_id
        address.city_id=city_id
        address.district_id=district_id
        address.save()
        address_dict = {
            "id":id,
            "receiver": receiver,
            "province": province,
            "city": city,
            "district": district,
            "place":place,
            "mobile":mobile,
            "tel":tel,
            "email":email
        }
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '修改地址成功', 'address': address_dict})
    # 删除地址
    def delete(self,request,address_id):
        # Address.objects.get(id=address_id).delete()
        # 逻辑删除收获地址
        try:
            # 查询要删除的地址
            address = Address.objects.get(id=address_id)

            # 将地址逻辑删除设置为True
            address.is_deleted = True
            address.save()
        except Exception as e:
            logger.error(e)
            return http.JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})
        return http.JsonResponse({'code':RETCODE.OK,'errmsg':'删除地址成功'})

# 设置默认地址

class DeAddressView(LoginRequiredMixin, View):
    def put(self,request,address_id):
        address = Address.objects.get(id=address_id)
        address.user.default_address_id = address_id
        address.user.save()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认地址成功'})

# 设置title

class TiAddressView(LoginRequiredMixin, View):
    def put(self,request,address_id):
        josn_ti = request.body.decode()
        data = json.loads(josn_ti)
        title = data["title"]
        address = Address.objects.get(id=address_id)
        address.title = title
        address.save()
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '设置title成功'})

# 用户浏览记录

class UserBrowseHistory(LoginRequiredMixin, View):
    # 保存用户浏览记录(redis只保存sku-id)
    def post(self, request):
        # 接受参数
        sku_id = json.loads(request.body.decode())["sku_id"]
        # 校验
        try:
            sku = SKU.objects.get(id=sku_id)
        except:
            return http.HttpResponseForbidden('商品不存在')
        # 链接redis
        client = get_redis_connection("history")
        redis_key = 'history_%s'%request.user.id
        p1 = client.pipeline()
        # 去重
        p1.lrem(redis_key,0,sku_id)
        # 存数据库
        p1.lpush(redis_key,sku_id)
        # 截取返回
        p1.ltrim(redis_key,0,4)
        # 启动
        p1.execute()
        # 响应结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
    # 返回用户浏览记录数据
    def get(self,request):
        # 链接数据库
        client = get_redis_connection("history")
        # 查数据  redis 取出来的是字节
        sku_ids = client.lrange('history_%d'%request.user.id,0,-1)
        sku_list = []
        # 通过sku_id转换成sku
        for sku_id in sku_ids:
            sku = SKU.objects.get(id=sku_id)
        # 封装在城前端数据格式
            sku_list.append({
                'id': sku.id,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price
            })
        # 返回响应对象
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'skus': sku_list})


